from datetime import datetime
import random
import re
from flask import request, jsonify, current_app, abort, make_response, session
from ihome import redis_store, constants, db
from ihome.libs.yuntongxun.sms import CCP
from ihome.models import User
from ihome.response_code import RET
from ihome.api_1_0 import api
from ihome.utils.captcha.captcha import captcha
from ihome.utils.commons import login_required


# 127.0.0.1:5000/api/v1.0/session, 没有参数
@api.route('/session', methods=["DELETE"])
@login_required
def login_out():
    """退出登录"""

    # 1.删除session中的键值对数据
    session.pop("user_id", None)
    session.pop("name", None)
    session.pop("mobile", None)
    # 2.返回退出登录成功
    return jsonify(errno=RET.OK, errmsg="退出登录成功")


# 127.0.0.1:5000/api/v1.0/session
@api.route("/session", methods=["GET"])
def check_login():
    """获取当前用户登录状态"""
    user_id = session.get("user_id")
    name = session.get("name")
    if not name:
        return jsonify(errno=RET.SESSIONERR, errmsg="用户未登录")

    return jsonify(errno=RET.OK, errmsg="OK",data={"user_id":user_id, "name":name})


# 127.0.0.1:5000/api/v1.0/session
@api.route('/session', methods=["POST"])
def login():
    """登录的后端接口"""

    """
    1.获取参数
        1.1 mobile:手机号码， password:密码
    2.参数校验
        2.1 非空判断
        2.2 手机号码格式判断
    3.逻辑处理
        3.1 根据手机号码查询当前用户对象
        3.2 用户存在，进行密码对比
        3.2.1 修改最后一次登录时间
        3.3 登录成功记录用户信息
    4.返回值
        4.1 登录成功
    """

    # 1.获取参数
    #     1.1 mobile:手机号码， password:密码
    mobile = request.json.get("mobile")
    password = request.json.get("password")

    # 2.参数校验
    #     2.1 非空判断
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    #     2.2 手机号码格式判断
    if not re.match(r"1[3456789][0-9]{9}", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式错误")

    # 3.逻辑处理
    #     3.1 根据手机号码查询当前用户对象
    user = None
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户对象异常")
    if not user:
        # 用户不存在
        return jsonify(errno=RET.USERERR, errmsg="用户不存在")

    #     3.2 用户存在，进行密码对比
    if not user.check_passowrd(password):
        # 密码填写错误
        return jsonify(errno=RET.DATAERR, errmsg="密码填写错误")

    #     3.2.1 修改最后一次登录时间
    user.last_login = datetime.now()
    # 将用户修改操作提交到数据库
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 数据库回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="用户属性异常")

    #     3.3 登录成功记录用户信息
    session["name"] = user.name
    session["mobile"] = user.mobile
    session["user_id"] = user.id

    # 4.返回值
    #     4.1 登录成功
    return jsonify(errno=RET.OK, errmsg="登录成功")


# 127.0.0.1:5000/api/v1.0/user
@api.route('/user', methods=["POST"])
def register():
    """注册的后端接口"""

    """
    1.获取参数
        1.1 mobile:手机号码， phonecode:用户填写的短信验证码，password:未加密的密码
    2.参数校验
        2.1 非空判断
        2.2 手机号码格式判断
    3.逻辑处理
        3.1 根据手机号码作为key去redis数据库获取真实的短信验证码
            有值：将真实的短信验证码值从redis数据库删除
            没有值：短信验证码过期了
        3.2 对比用户填写的短信验证码值和真实的短信验证码值是否一致
            相等：注册
            不相等：填写的短信验证码错误
        3.3 注册：创建用户对象，并给各个属性赋值
        3.4 注册成功一般要求登录成功，使用session记录用户登录信息
    4.返回值
        4.1 返回注册成功
    """

    # 1.获取参数
    #     1.1 mobile:手机号码， phonecode:用户填写的短信验证码，password:未加密的密码
    param_dict = request.json
    mobile = param_dict.get("mobile")
    phonecode = param_dict.get("phonecode")
    password = param_dict.get("password")

    # 2.参数校验
    #     2.1 非空判断
    if not all([mobile, phonecode, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    #     2.2 手机号码格式判断
    if not re.match(r"1[3456789][0-9]{9}", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式错误")

    # 3.逻辑处理
    #     3.1 根据手机号码作为key去redis数据库获取真实的短信验证码
    try:
        real_sms_code = redis_store.get("SMS_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询短信验证码异常")

    #         有值：将真实的短信验证码值从redis数据库删除
    if real_sms_code:
        redis_store.delete("SMS_%s" % mobile)
    #         没有值：短信验证码过期了
    else:
        return jsonify(errno=RET.NODATA, errmsg="短信验证码过期")

    #     3.2 对比用户填写的短信验证码值和真实的短信验证码值是否一致
    #         不相等：填写的短信验证码错误
    if phonecode != real_sms_code:
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码填写错误")

    #         相等：注册
    #     3.3 注册：创建用户对象，并给各个属性赋值
    user = User()
    # 昵称
    user.name = mobile
    # 账号
    user.mobile = mobile
    # TODO：密码加密
    # user.set_password_hash(password)
    # 给属性赋值：触发的是属性的set方法
    user.password = password

    # 提取属性的值：触发的是属性的get方法
    # print(user.password)

    # 最后一次登录时间
    user.last_login = datetime.now()

    try:
        # 保存回数据库
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户对象异常")

    #     3.4 注册成功一般要求登录成功，使用session记录用户登录信息
    session["name"] = user.name
    session["mobile"] = user.mobile
    session["user_id"] = user.id

    # 4.返回值
    #     4.1 返回注册成功
    return jsonify(errno=RET.OK, errmsg="注册成功")


# 127.0.0.1:5000/api/v1.0/smscode  参数是借助请求体携带
@api.route('/smscode', methods=["POST"])
def send_smscode():
    """发送短信验证码的后端接口"""

    """
    1.获取参数
        1.1 mobile: 手机号， image_code: 图片验证码编号(即用户填写的验证码值)， image_code_id: 图片验证码内容（即UUID编号）
    2.校验参数
        2.1 非空判断
        2.2 手机号码格式校验
    3.逻辑处理
        3.1 根据image_code_id编号去redis中获取正确真实的图片验证码值
            3.1.1 真实的图片验证码有值：将值从redis数据库删除[避免拿着这个值多次判断]
            3.1.2 真实的图片验证码没有值：图片验证码值过期了
        3.2 对比用户填写的图片验证码值 & 正确真实的图片验证码值
        3.3 不相等：返回错误状态码，提示图片验证码填写错误
        TODO: 提前判断手机号码是否注册过，数据库查询【提高用户体验】
        3.4 发送短信验证码
            3.4.1 生成6位的随机短信验证码值
            3.4.2 调用CCP类发送短信验证码
            3.4.3 发送短信验证码成功后，保存6位的短信验证码值到redis数据库
    4.返回值
        4.1 发送短信验证码成功
    """

    # 1.获取参数
    #     1.1 mobile: 手机号， image_code: 图片验证码编号(即用户填写的验证码值)， image_code_id: 图片验证码内容（即UUID编号）
    param_dict = request.json
    mobile = param_dict.get("mobile")
    image_code = param_dict.get("image_code")
    image_code_id = param_dict.get("image_code_id")

    # 2.校验参数
    #     2.1 非空判断
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    #     2.2 手机号码格式校验
    if not re.match(r"1[3456789][0-9]{9}", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号码格式错误")

    # 3.逻辑处理
    #     3.1 根据image_code_id编号去redis中获取正确真实的图片验证码值
    try:
        real_image_code = redis_store.get("imageCode_%s" % image_code_id)
    except Exception as e:
        # 使用flask方式记录日志
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询图片验证码真实值数据异常")

    #         3.1.1 真实的图片验证码有值：将值从redis数据库删除[避免拿着这个值多次判断]
    if real_image_code:
        redis_store.delete("imageCode_%s" % image_code_id)
    #         3.1.2 真实的图片验证码没有值：图片验证码值过期了
    else:
        return jsonify(errno=RET.NODATA, errmsg="图片验证码值过期了")

    #     3.2 对比用户填写的图片验证码值 & 正确真实的图片验证码值
    #     3.3 不相等：返回错误状态码，提示图片验证码填写错误
    # 注意：忽略大小写 设置decode_responses=True
    if image_code.lower() != real_image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码填写错误")

    #     TODO: 提前判断手机号码是否注册过，数据库查询【提高用户体验】
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户对象异常")
    if user:
        # 用户已经注册
        return jsonify(errno=RET.DATAEXIST, errmsg="用户手机号码已经注册")

    #     3.4 发送短信验证码
    #         3.4.1 生成6位的随机短信验证码值
    real_sms_code = random.randint(0, 999999)
    # 不足6位前面补零
    real_sms_code = "%06d" % real_sms_code

    # #         3.4.2 调用CCP类发送短信验证码
    # ccp = CCP()
    # try:
    #     result = ccp.send_template_sms(mobile, [real_sms_code, constants.SMS_CODE_REDIS_EXPIRES / 60], 1)
    # except Exception as e:
    #     current_app.logger.error(e)
    #     return jsonify(errno=RET.THIRDERR, errmsg="云通信发送短信验证码异常")
    #
    # if result == -1:
    #     return jsonify(errno=RET.THIRDERR, errmsg="云通信发送短信验证码异常")

    #         3.4.3 发送短信验证码成功后，保存6位的短信验证码值到redis数据库
    try:
        redis_store.setex("SMS_%s" % mobile, constants.SMS_CODE_REDIS_EXPIRES, real_sms_code)

        # ------------------------------------测试时用-------------------------------------------------
        # 打印生成的短信验证码，可以注释掉调用CCP类发送短信验证码中的代码
        print(real_sms_code)
        # ----------------------------------------------------------------------------------------------

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码异常")

    # 4.返回值
    #     4.1 发送短信验证码成功
    return jsonify(errno=RET.OK, errmsg="发送成功")


# 127.0.0.1:5000/api/v1.0/imagecode
@api.route('/imagecode', methods=["GET"])
def generate_imagecode():
    """发送图形验证码的后端接口"""

    """
    1.获取参数
        1.1 cur: UUID唯一编号
    2.校验参数
        2.1 非空判断
    3.逻辑处理
        3.1 调用工具类，生成图形验证码图片，图形验证码的真实值
        3.2 以cur作为key图形验证码的真实值，存储到redis数据库
    4.返回值
        4.1 将图片数据返回
    """

    # 1.获取参数
    #     1.1 cur: UUID唯一编号
    cur = request.args.get('cur')

    # 2.校验参数
    #     2.1 非空判断
    if not cur:
        return abort(404)

    # 3.逻辑处理
    #     3.1 调用工具类，生成图形验证码图片，图形验证码的真实值
    image_name, real_image_code, image_data = captcha.generate_captcha()

    #     3.2 以cur作为key图形验证码的真实值，存储到redis数据库
    redis_store.setex("imageCode_%s" % cur, constants.IMAGE_CODE_REDIS_EXPIRES, real_image_code)

    # 4.返回值
    #     4.1 将图片数据返回
    # 注意：如果不设置响应数据格式，返回的就是普通文件数据，不能兼容所有浏览器
    response = make_response(image_data)
    # 将响应数据格式设置为：图片的png格式
    response.headers["Content-Type"] = "png/image"
    return response


@api.route('/index')
def index():
    return "index"